package edu.poly.game.control;

import java.util.List;
import java.util.Random;
import java.util.regex.Pattern;

import edu.poly.game.battleship.BattleShipType;
import edu.poly.game.config.Configuration;
import edu.poly.game.console.SystemConsole;
import edu.poly.game.player.MissileFireResult;
import edu.poly.game.player.MissileFireResultType;
import edu.poly.game.player.Player;
import edu.poly.game.stats.GameStats;
import edu.poly.game.util.Coordinate;
import edu.poly.game.util.Dimension;

public class GamePlayController {
	private static Pattern coordinatePattern = Pattern.compile("^[1-9]+\\s{1}[1-9]+$");
	private static Random random = new Random();
	
	private final List<Player> players;
	private final Dimension boardDimension;
	
	private final Configuration config;
	
	public GamePlayController(final List<Player> players, final Configuration config) {
		this.players = players;
		this.boardDimension = config.getBoardSize();
		this.config = config;
	}
	
	public void startGame() {
		Player computer = this.players.get(0);
		Player human = this.players.get(1);
		
		Player currentPlayer = human;
		Player opponentPlayer = computer;
		
		while(computer.hasMoreShips() && human.hasMoreShips()) {
			Coordinate coordinate = null;
			if(currentPlayer.isAutomatic()) {
				coordinate = autoGenerateCoordinate();
			} else {
				coordinate = getCoordinate();
			}
			MissileFireResult result = opponentPlayer.receiveMissileHit(coordinate);
			if(MissileFireResultType.HITDESTROYEDSHIP.equals(result.getResultType()) ||
					MissileFireResultType.ALREADYHITCOORDINATE.equals(result.getResultType())) {
				//user get another chance if entered coordinate is a destroyed ship
				//or already hitted that spot
				if(currentPlayer.isAutomatic() == false)
					SystemConsole.printMessage("You've already entered this coordinate before or the ship at this coordinate already destroyed.");
				continue;
			}
			
			currentPlayer.processResult(result);
			
			if(result.getResultType().equals(MissileFireResultType.MISSED)) {
				SystemConsole.printMessage(currentPlayer.getPlayerType().name() + " Missed.");
			} else if (result.getResultType().equals(MissileFireResultType.HIT)) {
				SystemConsole.printMessage(currentPlayer.getPlayerType().name() + " Hit " + opponentPlayer.getPlayerType().name() + "'s " + result.getShipType().getName());
			}
			
			Player tmp = opponentPlayer;
			opponentPlayer = currentPlayer;
			currentPlayer = tmp;
			
			SystemConsole.printGameGrid(human.getGameBoard(), computer.getGameBoard());
		}
		
		printGameResult(human, computer);
	}
	
	private void printGameResult(Player human, Player computer) {
		int humanScore = this.calculateScore(human.getGameStats());
		int computerScore = this.calculateScore(computer.getGameStats());
		
		if(humanScore > computerScore)
			humanScore += 50;
		else
			computerScore += 50;
		
		SystemConsole.printMessage("Human Player:");
		SystemConsole.printMessage(human.getGameStats().toString());
		SystemConsole.printMessage("Points Earned = " + humanScore);
		SystemConsole.printMessage("");
		SystemConsole.printMessage("Computer Player:");
		SystemConsole.printMessage(computer.getGameStats().toString());
		SystemConsole.printMessage("Points Earned = " + computerScore);
		
		if(humanScore > computerScore)
			SystemConsole.printMessage("Human is a Winner.");
		else if (humanScore < computerScore)
			SystemConsole.printMessage("Computer is a Winner.");
		else
			SystemConsole.printMessage("It is a tie game!.");
	}
	
	private Coordinate autoGenerateCoordinate() {
		return new Coordinate(random.nextInt(this.boardDimension.getWidth() - 1), random.nextInt(this.boardDimension.getHeight() - 1));
	}
	
	private Coordinate getCoordinate() {
		String coordinateInput = null;
		boolean reEnter = false;

		StringBuilder msg = new StringBuilder("Please enter coordinate for firing missile, e.g. 1 5: ");
		
		Coordinate coordinate = null;
		do {
			coordinateInput = SystemConsole.askForInput(msg.toString());
			if (coordinatePattern.matcher(coordinateInput).matches() == false) {
				reEnter = true;
			} else {
				String[] input = coordinateInput.split(" ");
				int x = Integer.parseInt(input[0]) - 1;
				int y = Integer.parseInt(input[1]) - 1;
				coordinate = new Coordinate(x, y);
				return coordinate;
			}

			if (reEnter) {
				SystemConsole.printMessage("You've entered invalid coordinate.");
			}
		} while (reEnter);
		return coordinate;
	}
	
	private int calculateScore(final GameStats stats) {
		List<BattleShipType> destroyedShips = stats.getDestroyedShips();
		int points = 0;
		
		if(destroyedShips != null && destroyedShips.size() > 0) {
			for(BattleShipType type : destroyedShips) {
				points += config.getShipConfig().get(type).getHeight() * 10;
			}
		}
		
		points += stats.getNumberOfMissilesLeft() * 10;
		
		return points;
	}
}
